﻿// Copyright (c) 2009 Daniel Grunwald
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using System.Xml;

using ICSharpCode.AvalonEdit.CodeCompletion;
using ICSharpCode.AvalonEdit.Folding;
using ICSharpCode.AvalonEdit.Highlighting;
using Microsoft.Win32;

namespace CPPEI.Coolzon.Designer.Views
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class JSEditor : UserControl
    {
        private SolutionTreeViewNode _node;
        public bool IsDirty { get; set; }
        public JSEditor(SolutionTreeViewNode node)
        {
            InitializeComponent();

            _node = node;
            if (node.NodeStatus == NodeStatus.Add || node.NodeStatus == NodeStatus.CheckOut)
            {
                textEditor.IsReadOnly = false;
            }
            else
            {
                textEditor.IsReadOnly = true;
            }
            _node.PropertyChanged += Node_PropertyChanged;

            // Load our custom highlighting definition
            IHighlightingDefinition customHighlighting;
            using (Stream s = typeof(JSEditor).Assembly.GetManifestResourceStream("CPPEI.Coolzon.Designer.Views.Windows.JSEditorWindow.CustomHighlighting.xshd"))
            {
                if (s == null)
                    throw new InvalidOperationException("Could not find embedded resource");
                using (XmlReader reader = new XmlTextReader(s))
                {
                    customHighlighting = ICSharpCode.AvalonEdit.Highlighting.Xshd.
                        HighlightingLoader.Load(reader, HighlightingManager.Instance);
                }
            }
            // and register it in the HighlightingManager
            HighlightingManager.Instance.RegisterHighlighting("Custom Highlighting", new string[] { ".cool" }, customHighlighting);


            //propertyGridComboBox.SelectedIndex = 2;

            //textEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("C#");
            //textEditor.SyntaxHighlighting = customHighlighting;
            // initial highlighting now set by XAML

            textEditor.TextArea.TextEntering += textEditor_TextArea_TextEntering;
            textEditor.TextArea.TextEntered += textEditor_TextArea_TextEntered;
            textEditor.Loaded += TextEditor_Loaded;

            DispatcherTimer foldingUpdateTimer = new DispatcherTimer();
            foldingUpdateTimer.Interval = TimeSpan.FromSeconds(2);
            foldingUpdateTimer.Tick += foldingUpdateTimer_Tick;
            foldingUpdateTimer.Start();
        }

        private void Node_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (string.Equals(e.PropertyName, "NodeStatus"))
            {
                if (_node.NodeStatus == NodeStatus.Add || _node.NodeStatus == NodeStatus.CheckOut)
                {
                    Dispatcher.Invoke(() =>
                    {
                        textEditor.IsReadOnly = false;
                    });
                }
                else
                {
                    Dispatcher.Invoke(() =>
                    {
                        textEditor.IsReadOnly = true;
                    });
                }
            }
        }

        string currentFileName;

        void openFileClick(object sender, RoutedEventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.CheckFileExists = true;
            if (dlg.ShowDialog() ?? false)
            {
                currentFileName = dlg.FileName;
                textEditor.Load(currentFileName);
                textEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinitionByExtension(Path.GetExtension(currentFileName));
            }
        }

        public void OpenFile(string fileName, string ext)
        {
            if (!string.IsNullOrWhiteSpace(fileName) && File.Exists(fileName))
            {
                currentFileName = fileName;
                textEditor.Load(currentFileName);
                textEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinitionByExtension(ext);
            }
        }

        private void TextEditor_Loaded(object sender, RoutedEventArgs e)
        {
            IsDirty = false;
        }

        void saveFileClick(object sender, EventArgs e)
        {
            Save();
        }

        public void Save()
        {
            if (currentFileName == null)
            {
                SaveFileDialog dlg = new SaveFileDialog();
                dlg.DefaultExt = ".txt";
                if (dlg.ShowDialog() ?? false)
                {
                    currentFileName = dlg.FileName;
                }
                else
                {
                    return;
                }
            }
            textEditor.Save(currentFileName);
            IsDirty = false;
        }

        void propertyGridComboBoxSelectionChanged(object sender, RoutedEventArgs e)
        {
            //if (propertyGrid == null)
            //	return;
            //switch (propertyGridComboBox.SelectedIndex) {
            //	case 0:
            //		propertyGrid.SelectedObject = textEditor;
            //		break;
            //	case 1:
            //		propertyGrid.SelectedObject = textEditor.TextArea;
            //		break;
            //	case 2:
            //		propertyGrid.SelectedObject = textEditor.Options;
            //		break;
            //}
        }

        CompletionWindow completionWindow;

        void textEditor_TextArea_TextEntered(object sender, TextCompositionEventArgs e)
        {
            if (e.Text == "." && !textEditor.IsReadOnly)
            {
                // open code completion after the user has pressed dot:
                completionWindow = new CompletionWindow(textEditor.TextArea);
                // provide AvalonEdit with the data:
                IList<ICompletionData> data = completionWindow.CompletionList.CompletionData;
                data.Add(new MyCompletionData("getScene()"));
                data.Add(new MyCompletionData("net"));
                data.Add(new MyCompletionData("getPart('')"));
                data.Add(new MyCompletionData("setPropertyXmlString('', '')"));
                data.Add(new MyCompletionData("getPropertyXmlString('')"));
                data.Add(new MyCompletionData("sendNetEvent('', '')"));
                data.Add(new MyCompletionData("setPropertyXmlString('', '', '')"));
                data.Add(new MyCompletionData("date"));
                data.Add(new MyCompletionData("now()"));
                data.Add(new MyCompletionData("toString('')"));
                data.Add(new MyCompletionData("addDay('')"));
                data.Add(new MyCompletionData("addMonth('')"));
                data.Add(new MyCompletionData("addYear('')"));
                data.Add(new MyCompletionData("getMonth()"));
                data.Add(new MyCompletionData("getYear()"));
                data.Add(new MyCompletionData("getItem('')"));
                data.Add(new MyCompletionData("value"));
                completionWindow.Show();
                completionWindow.Closed += delegate
                {
                    completionWindow = null;
                };
            }
        }

        void textEditor_TextArea_TextEntering(object sender, TextCompositionEventArgs e)
        {
            if (e.Text.Length > 0 && completionWindow != null && !textEditor.IsReadOnly)
            {
                if (!char.IsLetterOrDigit(e.Text[0]))
                {
                    // Whenever a non-letter is typed while the completion window is open,
                    // insert the currently selected element.
                    completionWindow.CompletionList.RequestInsertion(e);
                }
            }
            // do not set e.Handled=true - we still want to insert the character that was typed
        }


        #region Folding
        FoldingManager foldingManager;
        AbstractFoldingStrategy foldingStrategy;

        void HighlightingComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (textEditor.SyntaxHighlighting == null)
            {
                foldingStrategy = null;
            }
            else
            {
                switch (textEditor.SyntaxHighlighting.Name)
                {
                    case "XML":
                        foldingStrategy = new XmlFoldingStrategy();
                        textEditor.TextArea.IndentationStrategy = new ICSharpCode.AvalonEdit.Indentation.DefaultIndentationStrategy();
                        break;
                    case "C#":
                    case "C++":
                    case "PHP":
                    case "Java":
                        textEditor.TextArea.IndentationStrategy = new ICSharpCode.AvalonEdit.Indentation.CSharp.CSharpIndentationStrategy(textEditor.Options);
                        foldingStrategy = new BraceFoldingStrategy();
                        break;
                    default:
                        textEditor.TextArea.IndentationStrategy = new ICSharpCode.AvalonEdit.Indentation.DefaultIndentationStrategy();
                        foldingStrategy = new BraceFoldingStrategy();
                        break;
                }
            }
            if (foldingStrategy != null)
            {
                if (foldingManager == null)
                    foldingManager = FoldingManager.Install(textEditor.TextArea);
                foldingStrategy.UpdateFoldings(foldingManager, textEditor.Document);
            }
            else
            {
                if (foldingManager != null)
                {
                    FoldingManager.Uninstall(foldingManager);
                    foldingManager = null;
                }
            }
        }

        void foldingUpdateTimer_Tick(object sender, EventArgs e)
        {
            if (foldingStrategy != null)
            {
                foldingStrategy.UpdateFoldings(foldingManager, textEditor.Document);
            }
        }
        #endregion

        private void textEditor_KeyDown(object sender, KeyEventArgs e)
        {
            bool isCtrl = false;
            System.Windows.Input.KeyboardDevice kd = e.KeyboardDevice;
            if ((kd.GetKeyStates(Key.LeftCtrl) & System.Windows.Input.KeyStates.Down) > 0 ||
                (kd.GetKeyStates(Key.LeftCtrl) & System.Windows.Input.KeyStates.Down) > 0)
            {
                isCtrl = true;
            }
            if (isCtrl && e.Key == Key.S)
            {
                Save();
            }
        }

        private void textEditor_TextChanged(object sender, EventArgs e)
        {
            IsDirty = true;
        }

        private void UserControl_Unloaded(object sender, RoutedEventArgs e)
        {
            if(_node != null)
            {
                _node.PropertyChanged -= Node_PropertyChanged;
            }
        }
    }
}